#include "stdio.h"
#include "stdlib.h"
#include "memory.h"
#include "math.h"
#include "time.h"
#include "GAChromosome.h"
////////////////////////////////////////////////////////////////
//REF: An Evolutionary Approach To Tetris
//     Niko Bohm, Cabriella Kokai, Stefan Mandl
////////////////////////////////////////////////////////////////

GAChromosome::GAChromosome()
{
  m_nID = 0;
  m_nGene = 0;
  m_pGenes = 0;
  m_fScore = 0.0f;  
}
GAChromosome::GAChromosome(unsigned long ulID)
{
  m_nID = ulID;
  m_nGene = 0;
  m_pGenes = 0;
  m_fScore = 0.0f;
}

GAChromosome::GAChromosome(unsigned long ulID, int nGene)
{
  m_nID = ulID;
  m_fScore = 0.0f; 
  if(nGene > 0)
    {
      m_nGene = nGene;
      m_pGenes = new int[nGene];
    }
  else
    {
      m_nGene = 0;
      m_pGenes = NULL;
    }
}


GAChromosome::GAChromosome(unsigned long ulID, int nGene, const int* pGenes)
{
  init(ulID, nGene, pGenes);
}


void GAChromosome::init(unsigned long ulID, int nGene, const int* pGenes)
{
    m_nID = ulID;
    m_fScore = 0.0f;
    if(nGene > 0)
    {
        m_nGene = nGene;
	m_pGenes = new int[nGene];
	if(pGenes != NULL)
	  memcpy(m_pGenes, pGenes, nGene * sizeof(int));
    }
  else
    {
      m_nGene = 0;
      m_pGenes = NULL;
    }
    
}

void GAChromosome::clean(void)
{
  if(m_pGenes != NULL)
    delete[] m_pGenes;
  m_pGenes = NULL;
  m_nGene = 0;
  m_fScore = 0.0f;
}

GAChromosome::~GAChromosome(void)
{
  clean();
}

int& GAChromosome::operator[](const int index)
{
  return m_pGenes[index];
}

float& GAChromosome::Score(void)
{
  return m_fScore;
}


int GAChromosome::GetGeneCount()
{
  return m_nGene;
}

unsigned long GAChromosome::GetUniqueID()
{
  return m_nID;
}

void GAChromosome::SetUniqueID(unsigned long ulID)
{
  m_nID = ulID;
}

int GAChromosome::CopyOfGene(unsigned long ulID, GAChromosome* pSrc)
{
  clean();
  init(ulID, pSrc->GetGeneCount(), NULL);

  int index;
  for(index = 0; index < m_nGene; index++)
    {
      m_pGenes[index] = (*pSrc)[index];
    }

  return 0;
}

int& GAChromosome::Gene(int index)
{
  return m_pGenes[index];
}

int GAChromosome::SetGeneCount(int nCount)
{
  if(nCount < 0)
	return -1;
  unsigned long ulID = m_nID;
  clean();
  init(ulID, nCount, NULL);
  return 0;
}

/////////////////////////////////////////////////////////////////////////

int GACrossOver_I(GAChromosome* pFstParent, GAChromosome* pSecParent,
				GAChromosome* pFstChild, GAChromosome* pSecChild)
{
  int index;
  int nGene = pFstParent->GetGeneCount();
  int nStart = rand() % nGene;
  int nEnd = rand() % nGene;

  //1--copy genes from parents
  pFstChild->CopyOfGene(pFstChild->GetUniqueID(), pFstParent);
  pSecChild->CopyOfGene(pSecChild->GetUniqueID(), pSecParent);

  //printf("from %d to %d\n", nStart, nEnd);
  //2--exchange genes between nStart and nEnd (circle)
  index = nStart;
  do{

	//first child
	pFstChild->Gene(index) = pSecParent->Gene(index);

	//second child
	pSecChild->Gene(index) = pFstParent->Gene(index);


	index ++;
	if(index >= nGene)
	  {
		index -= nGene; //treat chromosome as a circle
	  }
	
  }while(index != nEnd); //issue: if nStart == nEnd, all gene is exchanged
  
  
  
  return 0;
}

typedef int (*GA_CROSSOVER_FUNC) (GAChromosome*, GAChromosome*,
								  GAChromosome*, GAChromosome*);

GA_CROSSOVER_FUNC gb_CrossOver_Func[2] =
  {
	NULL,
	GACrossOver_I
  };

//run cross-over operation on two specified chromosomes
/*
  \param           pFstParent        parent I
  \param           pSecParent        parent II
  \param           pFstChild[out]    child I
  \param           pSecChild[out]    child II
  \param           nType             cross-over type
  \return          0                 ok
  \return          < 0               error
 */
int GACrossOver(GAChromosome* pFstParent, GAChromosome* pSecParent,
		GAChromosome* pFstChild, GAChromosome* pSecChild, int nType)
{
  if(nType < 1 || nType > 1)
	return -1;
  
  if(pFstParent == NULL || pSecParent == NULL ||
	 pFstChild == NULL || pSecChild == NULL)
	return -1;
  
  if(pFstParent->GetGeneCount() != pSecParent->GetGeneCount() ||
	 pFstChild->GetGeneCount() != pSecChild->GetGeneCount() ||
	 pFstChild->GetGeneCount() != pFstParent->GetGeneCount() )
	return -1;
  
  return gb_CrossOver_Func[nType](pFstParent, pSecParent, pFstChild,
								  pSecChild);
}

int GAMutation_I(GAChromosome* pChromosome)
{
  float fMutationPro = 0.5f;
  int index;
  if(pChromosome == NULL)
	return -1;

  for(index = 0; index < pChromosome->GetGeneCount(); index++)
	{
	  if(rand() % 1000 < 1000 * fMutationPro)
		continue; //don't run mutation

	  int v = rand() % 2000 - 1000;
	  float factor = v / 100.0f;
	  pChromosome->Gene(index) *= factor;
		
	}

  return 0;
}


typedef int (*GA_MUTATION_FUNC)(GAChromosome*);
GA_MUTATION_FUNC gb_Mutation_Func[2] =
{
  NULL,
  GAMutation_I
};
//run mutation on specified chromosome
/*
  \param          pChromosome[in/out]        chromosome operated on
  \param          nType                      Mutation type
  \return         0                          ok
  \return         < 0                        error
 */
int GAMutation(GAChromosome* pChromosome, int nType)
{
  if(nType < 1 || nType > 1)
	return -1;
  return gb_Mutation_Func[nType](pChromosome);
}

//run scale on specified chromosome
/*
  \param		pChromosome		chromosome operated on
  \param		fFactor			factor
  \return       0				ok
  \return       < 0				error
 */
int GAScale(GAChromosome* pChromosome, float fFactor)
{
  if(pChromosome == NULL)
	return -1;

  int index;
  for(index = 0; index < pChromosome->GetGeneCount(); index++)
	{
	  (*pChromosome)[index] *= fFactor;
	}

  return 0;
}

int SaveChromosome(FILE*& pHandle, GAChromosome* pChromosome)
{
  if(pChromosome == NULL || pHandle == NULL)
	return -1;
  fprintf(pHandle, "%ld\t%f\t%d\n", pChromosome->GetUniqueID(),
		  pChromosome->Score(), pChromosome->GetGeneCount());
  int index;
  for(index = 0; index < pChromosome->GetGeneCount(); index++)
	{
	  fprintf(pHandle, "%d\t", pChromosome->Gene(index));
	}
  fprintf(pHandle, "\n");
  return 0;
}

int LoadChromosome(FILE*& pHandle, GAChromosome* pChromosome)
{
  if(pHandle == NULL || pChromosome == NULL)
	return -1;
  int ret;
  unsigned long nID;
  int nGene;
  float fScore;
  ret = fscanf(pHandle, "%ld\t%f\t%d\n",&nID, &fScore, &nGene);
  if(ret != 3 || nGene < 1)
	return -1;
  

  pChromosome->SetUniqueID(nID);
  pChromosome->SetGeneCount(nGene);

  int index;
  for(index = 0; index < nGene; index++)
	{
	  int nGene;
	  ret = fscanf(pHandle, "%d\t", &nGene);
	  if(ret != 1)
		return -1;
	  pChromosome->Gene(index) = nGene;
	}

  return 0;
}
